	/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		sdeditg dll.cpp
 * PURPOSE:		init/free support for module resources and dll entry
 *				for SD Edit GUI
 */

CRITICAL_SECTION GlobalGuiLibResourcesLock; //initialized during module load

struct GuiLibResources;

GuiLibResources *GlobalGuiLibResources; //for safe initialization only outside DllMain

struct GuiLibResources : auto_refctd_std_base //resources tied to dll lifetime, including those that can't be collected during DllMain
{
	AclEditor::WindowClassLifetime AclEditorClass;
	SdEditor::WindowClassLifetime SdEditorClass;

	GuiLibResources()
	{
		_set_invalid_parameter_handler(&CRT_InvalidParameterHandler);
		_CrtSetReportMode(_CRT_ASSERT, 0);
		_set_se_translator(&SEHTranslatorFunction);

		ErrorTaskDialog::TaskDialogIndirect_delayed.DemandQuiet();

		ShowErrorMessageBoxMessageBox = ErrorTaskDialog::TaskDialogIndirect_delayed.Func ? &ErrorTaskDialog::ShowErrorMessageBox : &ErrorDialog::ShowErrorMessageBox;

		InitExptHelp();

		InitSidText();

		InitWinBaseHelp();

		AclEditor::InitDefaultColumnDescriptions();	

		WorkQueue::Init();

		ThreadSynchEvent::InitThreadSynchEvents();
	}

	void inc_ref() //inc is safe, either ref sharing or lock already held by ReferenceGlrOnce
	{
		auto_refctd_std_base::inc_ref();
	}

	void dec_ref() //object can't go away or be created unless holding lock
	{
		EnterCriticalSection(&GlobalGuiLibResourcesLock);

		auto_refctd_std_base::dec_ref();

		LeaveCriticalSection(&GlobalGuiLibResourcesLock);
	}

	~GuiLibResources()
	{
		WorkQueue::Cleanup();

		FreeExptHelp();

		FreeWinBaseHelp();

		ThreadSynchEvent::CleanupThreadSynchEvents();

		__FUnloadDelayLoadedDLL2("wldap32.dll");
		__FUnloadDelayLoadedDLL2("secur32.dll");
		__FUnloadDelayLoadedDLL2("netapi32.dll");

		GlobalGuiLibResources = NULL; //GlobalGuiLibResourcesLock held by dec_ref() further up stack
	}
};

GuiLibResources *ReferenceGlr() //tie lifetime of Glr to your need of the module
{
	EnterCriticalSection(&GlobalGuiLibResourcesLock);

	if(!GlobalGuiLibResources)
		GlobalGuiLibResources = new GuiLibResources();

	LeaveCriticalSection(&GlobalGuiLibResourcesLock);

	return GlobalGuiLibResources;
};

BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved)
{
	switch(Reason)
	{
	case DLL_PROCESS_ATTACH:
		InitializeCriticalSection(&GlobalGuiLibResourcesLock);
		SdEditGModule = Instance;
		break;

	case DLL_PROCESS_DETACH:
		if(GlobalGuiLibResources)
			return FALSE;

		DeleteCriticalSection(&GlobalGuiLibResourcesLock);

#ifdef _DEBUG
		//DBG_CheckAllocs();
#endif
		break;
	}

	return TRUE;
}

typedef ObjectTypes::Object *(CALLBACK *OpenObjectByNameFunc)(SDE_GENERIC_OPTS const &GOpts, SDE_OPTS *Options, IN OUT Lookup::Translators &Translators, UCHAR Privileges);

enum GlobalOptionIndex //synch with CommandInstance::GlobalOptionsTemplate
{
	GoiName,
	GoiType,
	GoiTypeClass,
	GoiHost,
	GoiDomain,
	GoiCaseSense,
	GoiPrivPack,
	GoiGuiPack,
	GoiLsaPack,
	GoiLdapPack,
	GoiNetapiPack,
	GoiShellinstPack,
	GoiCount
};

struct ModuleResourceAccumulator : Module::ModuleResourceDest
{
	Lookup::Translators Translators;
	Lookup::Enumerators Enumerators;
	auto_refct<Lookup::MultiTranslator<SID> > MSidTrans;
	auto_refct<Lookup::MultiTranslator<GUID> > MGuidTypeTrans;
	auto_refct<Lookup::MultiTranslator<GUID> > MGuidInhTypeTrans;
	auto_refct<Lookup::MultiDomainEnumerator> MSidEnum;
	auto_refct<ObjectTypes::Object> Subject;

	ModuleResourceAccumulator() :
		MSidTrans(new Lookup::MultiTranslator<SID>()),
		MGuidTypeTrans(new Lookup::MultiTranslator<GUID>()),
		MGuidInhTypeTrans(new Lookup::MultiTranslator<GUID>()),
		MSidEnum(new Lookup::MultiDomainEnumerator())
	{
		Translators.SidTranslator = MSidTrans;
		Translators.GuidTypeTranslator = MGuidTypeTrans;
		Translators.GuidInhTypeTranslator = MGuidInhTypeTrans;

		Enumerators.SidEnumerator = MSidEnum;

		//add common resources
		{			
			auto_refct<Lookup::Enumerator<SID> > TokenEnum = new StdSid::TokenSidEnumerator();
			MSidEnum->AddSimpleEnum(L"Token", TokenEnum);

			auto_refct<Lookup::Enumerator<SID> > NonUnique = new StdSid::WellKnownNonUniqueSidEnumerator();
			MSidEnum->AddSimpleEnum(L"Non Unique", NonUnique);

			MSidTrans->Add(new StdSid::WellKnownNonUniqueSidTranslator(), L"Non unique");
			MSidTrans->Add(new StdSid::LogonSidTranslator(), L"Logon session");
		}
	}

	virtual void BasicSidEnumerator(PCWSTR Name, Lookup::Enumerator<SID> *Enum) override
	{
		MSidEnum->AddSimpleEnum(Name, Enum);
	}
	virtual void DomainSidEnumerator(Lookup::DomainAccountEnumerator *Enum) override
	{
		MSidEnum->AddDomainContentEnum(Enum);
	}
	virtual void DomainEnumerator(PCWSTR Name, Lookup::DomainEnumerator *Enum) override
	{
		MSidEnum->AddDomainEnum(Enum);
	}

	virtual void GuidTypeEnumerator(PCWSTR Name, Lookup::Enumerator<GUID> *Enum) override
	{
		if(!Enumerators.GuidTypeEnumerator)
			Enumerators.GuidTypeEnumerator = Enum;
	}
	virtual void GuidInhTypeEnumerator(PCWSTR Name, Lookup::Enumerator<GUID> *Enum) override
	{
		if(!Enumerators.GuidInhTypeEnumerator)
			Enumerators.GuidInhTypeEnumerator = Enum;
	}

	virtual void SidTranslator(PCWSTR Name, Lookup::Translator<SID> *Trn) override
	{
		MSidTrans->Add(Trn, Name);
	}
	virtual void GuidTypeTranslator(PCWSTR Name, Lookup::Translator<GUID> *Trn) override
	{
		MGuidTypeTrans->Add(Trn, Name);
	}
	virtual void GuidInhTypeTranslator(PCWSTR Name, Lookup::Translator<GUID> *Trn) override
	{
		MGuidInhTypeTrans->Add(Trn, Name);
	}
	virtual void Object(ObjectTypes::Object *Object)
	{
		Subject = Object;
	}
};

void AddResourcesFrom(Module::OPEN_STATUS &OpenStatus, Module::ModuleObject *Mod, SDE_GENERIC_OPTS const &GenericOpts, Module::ModuleResourceDest &ModRes, UCHAR Privileges, BOOLEAN AskToOpen)
{
	OpText Ot(L"Getting module resources", Mod->ModuleName());

	CppCancelOneHandlerExistance CCOHE; 
	try
	{
		Mod->OpenResources(OpenStatus, GenericOpts, ModRes, Privileges, AskToOpen);
	}
	catch(CancelOneException)
	{}

	Ot;
}


struct CommandInstance
{
	auto_refct<Module::ModuleObject> Modules[6];

	SDE_GENERIC_OPTS GenericOpts;

	SDE_GUI_PREFS *GuiPrefs;

	auto_SDE_OPTS GlobalOpts;

	auto_ptr<GuiOpt::GuiPrefsTranslated> TransGuiPrefs;

	void InitModules()
	{
		Modules[0] = new Win32::Win32Module();
		Modules[1] = new Ntapi::NtapiModule();
		Modules[2] = new Ldap::NtdsLdapModule();
		Modules[3] = new Netapi::NetapiModule();
		Modules[4] = new Lsa::LsaModule();
		Modules[5] = new Shellinst::ShellinstModule();
	}

	void InitGlobalOpts()
	{
		//create a temporary layout for the root options, to be copied into GlobalOpts

		SDE_OPT GlobalOptionsTemplate[] =
		{
			{L"n", 0, SptString},	//name
			{L"t", 0, SptString},	//type
			{L"tc", 0, SptString},	//type class
			{L"h", 0, SptString},	//host
			{L"d", 0, SptString},	//domain
			{L"cs", 0, SptBool},	//case sensitive
			//sub packages
			{L"priv", SDE_OPT_SET, SptSubList, &PrivOpts},
			{L"gui", SDE_OPT_SET, SptSubList, &GuiOpt::Opts},
			{L"lsa", 0, SptSubList},
			{L"ldap", 0, SptSubList},
			{L"netapi", 0, SptSubList},
			{L"shellhand", 0, SptSubList}
		};

		SDE_OPTS GlobalOptsTemplate =
		{
			RTL_NUMBER_OF(GlobalOptionsTemplate),
			GlobalOptionsTemplate
		};

		//give the module a copy of the basic tree
		GlobalOpts = GlobalOptsTemplate;

		//splice on module-specific option trees
		GlobalOpts.Opts[GoiLdapPack].SubList = &Modules[2]->ModuleOptions();
		GlobalOpts.Opts[GoiNetapiPack].SubList = &Modules[3]->ModuleOptions();
		GlobalOpts.Opts[GoiLsaPack].SubList = &Modules[4]->ModuleOptions();
		GlobalOpts.Opts[GoiShellinstPack].SubList = &Modules[5]->ModuleOptions();

		//module option packs now set
		for(ULONG i = GoiLdapPack; i <= GoiShellinstPack; i++)
			GlobalOpts.Opts[i].Flags |= SDE_OPT_SET;
	}

	void TranscribeGenericOpts()
	{
		SDE_GENERIC_OPTS &Go = GenericOpts;
		SDE_OPT *GlobalOptions = GlobalOpts.Opts;

		RtlZeroLengthedStruct(Go);
		Go.ObjName = GlobalOptions[GoiName].String;
		Go.ObjType = GlobalOptions[GoiType].String;
		Go.Host = GlobalOptions[GoiHost].String;
		Go.Domain = GlobalOptions[GoiDomain].String;
		if(GlobalOptions[GoiCaseSense].Flags & SDE_OPT_SPECIFIED)
		{
			Go.CaseSensitiveSpecified = TRUE;
			Go.CaseSensitive = GlobalOptions[GoiCaseSense].Bool;
		}
	};

	void LoadExtraKeyOptions(PCWSTR Name)
	{
		OpText Ot(L"Processing registry options");

		RegOptions::Populate(&GlobalOpts, Name);

		Ot;
	}
	
	CommandInstance(PWSTR CmdLine OPTIONAL)
	{
		InitModules();
		InitGlobalOpts();

		//load options
		{
			OpText Ot(L"Processing registry options");

			RegOptions::Populate(&GlobalOpts);

			Ot;
		}
		
		if(CmdLine)
		{
			OpText Ot(L"Processing command line");

			CmdLineParser::PopulatePrefsFromCmdLine(&GlobalOpts, CmdLine);

			Ot;
		}
	}

	void FinishInit() //call after constructor and after manual option specification
	{
		TranscribeGenericOpts();

		TransGuiPrefs = auto_take_(new GuiOpt::GuiPrefsTranslated(GlobalOpts.Opts[GoiGuiPack].SubList));
		GuiPrefs = &TransGuiPrefs->Prefs;
	}

	void AddAllModuleResources(ModuleResourceAccumulator &ModRes, UCHAR Privileges OPTIONAL)
	{
		Module::OPEN_STATUS OpenStatus;

		for(ULONG i = 0; i < RTL_NUMBER_OF(Modules); i++)
		{
			OpenStatus = 0;
			AddResourcesFrom(OpenStatus, Modules[i], GenericOpts, ModRes, Privileges, FALSE);
		}
	}
};